#--code:utf-8 -----
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
import json
from pyecharts import options as opts
from pyecharts.charts import Radar
from pyecharts.charts import Line, HeatMap
from pyecharts.charts import Grid, Page
import requests

app = Flask(__name__)

# 配置SQLite数据库连接
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///CP.db'

# 创建数据库对象
db = SQLAlchemy(app)


# 定义中奖数字表结构
class Numbers(db.Model):
    # id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    lotteryDrawNum = db.Column(db.String(80), unique=True, nullable=False, primary_key=True)  # 期号
    type = db.Column(db.Integer, nullable=False)  # 类型：1、大乐透；2、双色球；
    draw_date = db.Column(db.String(10), nullable=False)  # 开奖日期
    parity_ratio = db.Column(db.String(10), nullable=True)  # 奇偶比
    Interval_ratio = db.Column(db.String(10), nullable=True) # 区间比
    f_1 = db.Column(db.Integer, nullable=True)  # 前区
    f_2 = db.Column(db.Integer, nullable=True)
    f_3 = db.Column(db.Integer, nullable=True)
    f_4 = db.Column(db.Integer, nullable=True)
    f_5 = db.Column(db.Integer, nullable=True)
    f_6 = db.Column(db.Integer, nullable=True)
    l_1 = db.Column(db.Integer, nullable=True)  # 后区
    l_2 = db.Column(db.Integer, nullable=True)


# 定义数字表(错误设计）
"""
class Numbers_err(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    lotteryDrawNum = db.Column(db.String(80), unique=True, nullable=False)  # 期号
    type = db.Column(db.Integer, nullable=False)  # 类型：1、大乐透；2、双色球；
    parity_ratio = db.Column(db.String(10), unique=True, nullable=True)  # 奇偶比
    Interval_ratio = db.Column(db.String(10), unique=True, nullable=True) # 区间比
    f_01 = db.Column(db.Boolean, default=False, nullable=True)  # 前区
    f_02 = db.Column(db.Boolean, default=False, nullable=True)
    f_03 = db.Column(db.Boolean, default=False, nullable=True)
    f_04 = db.Column(db.Boolean, default=False, nullable=True)
    f_05 = db.Column(db.Boolean, default=False, nullable=True)
    f_06 = db.Column(db.Boolean, default=False, nullable=True)
    f_07 = db.Column(db.Boolean, default=False, nullable=True)
    f_08 = db.Column(db.Boolean, default=False, nullable=True)
    f_09 = db.Column(db.Boolean, default=False, nullable=True)
    f_10 = db.Column(db.Boolean, default=False, nullable=True)
    f_11 = db.Column(db.Boolean, default=False, nullable=True)
    f_12 = db.Column(db.Boolean, default=False, nullable=True)
    f_13 = db.Column(db.Boolean, default=False, nullable=True)
    f_14 = db.Column(db.Boolean, default=False, nullable=True)
    f_15 = db.Column(db.Boolean, default=False, nullable=True)
    f_16 = db.Column(db.Boolean, default=False, nullable=True)
    f_17 = db.Column(db.Boolean, default=False, nullable=True)
    f_18 = db.Column(db.Boolean, default=False, nullable=True)
    f_19 = db.Column(db.Boolean, default=False, nullable=True)
    f_20 = db.Column(db.Boolean, default=False, nullable=True)
    f_21 = db.Column(db.Boolean, default=False, nullable=True)
    f_22 = db.Column(db.Boolean, default=False, nullable=True)
    f_23 = db.Column(db.Boolean, default=False, nullable=True)
    f_24 = db.Column(db.Boolean, default=False, nullable=True)
    f_25 = db.Column(db.Boolean, default=False, nullable=True)
    f_26 = db.Column(db.Boolean, default=False, nullable=True)
    f_27 = db.Column(db.Boolean, default=False, nullable=True)
    f_28 = db.Column(db.Boolean, default=False, nullable=True)
    f_29 = db.Column(db.Boolean, default=False, nullable=True)
    f_30 = db.Column(db.Boolean, default=False, nullable=True)
    f_31 = db.Column(db.Boolean, default=False, nullable=True)
    f_32 = db.Column(db.Boolean, default=False, nullable=True)
    f_33 = db.Column(db.Boolean, default=False, nullable=True)
    f_34 = db.Column(db.Boolean, default=False, nullable=True)
    f_35 = db.Column(db.Boolean, default=False, nullable=True)
    b_01 = db.Column(db.Boolean, default=False, nullable=True)  # 后区
    b_02 = db.Column(db.Boolean, default=False, nullable=True)
    b_03 = db.Column(db.Boolean, default=False, nullable=True)
    b_04 = db.Column(db.Boolean, default=False, nullable=True)
    b_05 = db.Column(db.Boolean, default=False, nullable=True)
    b_06 = db.Column(db.Boolean, default=False, nullable=True)
    b_07 = db.Column(db.Boolean, default=False, nullable=True)
    b_08 = db.Column(db.Boolean, default=False, nullable=True)
    b_09 = db.Column(db.Boolean, default=False, nullable=True)
    b_10 = db.Column(db.Boolean, default=False, nullable=True)
    b_11 = db.Column(db.Boolean, default=False, nullable=True)
    b_12 = db.Column(db.Boolean, default=False, nullable=True)
    b_13 = db.Column(db.Boolean, default=False, nullable=True)
    b_14 = db.Column(db.Boolean, default=False, nullable=True)
    b_15 = db.Column(db.Boolean, default=False, nullable=True)
    b_16 = db.Column(db.Boolean, default=False, nullable=True)
"""

# 数字属性表结构
class NumbersAttribute(db.Model):
    lotteryDrawNum = db.Column(db.String(80), unique=True, nullable=False, primary_key=True)  # 期号
    number_id = db.Column(db.Integer)  # 号码
    thermal_value = db.Column(db.Integer, nullable=True)  # 冷热程度
    freq = db.Column(db.Float, nullable=True)  # 出现的频率
    prediction_coefficient = db.Column(db.Integer, nullable=True)  # 预测出现的权重

# 预测表结构
class PredictionNumbers(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    lotteryDrawNum = db.Column(db.String(80), unique=True, nullable=False)  # 期号
    prediction_number = db.Column(db.String(80), unique=True, nullable=False)  # 预测号


def get_result_data(type=1,file=None):
    # 获取中奖信息，支持文件读取，或者直接传数据进来做解释
    # type，读取类型：1:文件读取，2:JSON字符串解释
    if type == 1:
        with open(file, 'r', encoding='utf-8') as file:
            json_data = json.load(file)
    else:
        json_data = file

    result_data = {}
    # 大乐透和双色球的标识，1 大乐透； 2 双色球
    lottery_flag = 1
    try:
        # 大乐透数据提取
        value_data = json_data['value']
        for j in (value_data["list"]):
            # print(j)
            lotteryDrawNum = j["lotteryDrawNum"]  # 期号
            lotteryDrawResult = j["lotteryDrawResult"]  # 中奖结果
            DrawResult_list = lotteryDrawResult.split()
            lotteryDrawTime = j["lotteryDrawTime"]  # 开奖日期
            # print(lotteryDrawNum, lotteryDrawResult, lotteryDrawTime)
            # print(DrawResult_list[:5])
            # print(DrawResult_list[-2:])
            # print(lotteryDrawNum, ":", DrawResult_list)
            # 字典：{"期号": {"前区":xxx, "后区":xxx}
            result_data[lotteryDrawNum] = {"first": DrawResult_list[:5], "last": DrawResult_list[-2:], "draw_date":lotteryDrawTime}
    except:
        lottery_flag = 2

    if lottery_flag == 2:
        # 双色球数据提取
        value_data = json_data['result']
        for k in value_data:
            code = k["code"] # 期号
            red = k["red"] # 红色球
            red_list = red.split(",")
            blue = k["blue"] # 蓝色球
            lotteryDrawTime = k["date"]  # 开奖日期
            result_data[code] = {"first": red_list, "last": [blue], "draw_date":lotteryDrawTime}
    return {"value": result_data, "type" : lottery_flag}


def get_winner_data(type=1, span=20):
    # 从网络上获取中奖信息，参数：类型<1.大乐透；2.双色球>，查多少期<默认20>
    data = ""
    span = str(span)
    if type == 1:
        url = r"https://webapi.sporttery.cn/gateway/lottery/getHistoryPageListV1.qry?gameNo=85&provinceId=0&pageSize=" \
              + span + "&isVerify=1&pageNo=1&termLimits=" + span
    elif type == 2:
        url = r"http://www.cwl.gov.cn/cwl_admin/front/cwlkj/search/kjxx/findDrawNotice?name=ssq&issueCount=" + span + \
              "&issueStart=&issueEnd=&dayStart=&dayEnd=&pageNo=1&pageSize="+ span +"&week=&systemType=PC"
    else:
        return False
    response = requests.get(url)
    # print(response.text)
    # 检查请求是否成功
    if response.status_code == 200:
        # 解析 JSON 响应
        json_data = response.json()

        # 返回 JSON 数据
        # return jsonify(json_data)
        return json_data
    else:
        return False


def double_chromosphere_winning_rule(f_cnt=0, l_cnt=0):
    # 双色球中奖规则, 前区中奖数， 后区中奖数
    if f_cnt == 6 and l_cnt == 1:
        return 1
    elif f_cnt == 6 and l_cnt == 0:
        return 2
    elif f_cnt == 5 and l_cnt == 1:
        return 3
    elif f_cnt == 5 and l_cnt == 0:
        return 4
    elif f_cnt == 4 and l_cnt == 1:
        return 4
    elif f_cnt == 4 and l_cnt == 0:
        return 5
    elif f_cnt == 3 and l_cnt == 1:
        return 5
    elif f_cnt < 3 and l_cnt == 1:
        return 6
    else:
        return 0

def lottery_winning_rule(f_cnt=0, l_cnt=0):
    # 大乐透中奖规则, 前区中奖数， 后区中奖数
    if f_cnt == 5 and l_cnt == 2:
        return 1
    elif f_cnt == 5 and l_cnt == 1:
        return 2
    elif f_cnt == 5 and l_cnt == 0:
        return 3
    elif f_cnt == 4 and l_cnt == 2:
        return 4
    elif f_cnt == 4 and l_cnt == 1:
        return 5
    elif f_cnt == 3 and l_cnt == 2:
        return 6
    elif f_cnt == 4 and l_cnt == 0:
        return 7
    elif f_cnt == 3 and l_cnt == 1:
        return 8
    elif f_cnt == 2 and l_cnt == 2:
        return 8
    elif f_cnt == 3 and l_cnt == 0:
        return 9
    elif f_cnt == 1 and l_cnt == 2:
        return 9
    elif f_cnt == 2 and l_cnt == 1:
        return 9
    elif f_cnt == 0 and l_cnt == 2:
        return 9
    else:
        return 0

def winning_check(numbers=None):
    # 中奖信息查询
    # 传入的信息包括：期号，号码（列表），彩票类型（1:大乐透，2：双色球）
    if not numbers:
        return None
    with app.app_context():
        win_number = Numbers.query.filter_by(lotteryDrawNum = numbers["lotteryDrawNum"], type =numbers["type"]).first()
        if numbers["type"] == 1:
        # 大乐透前区5个，后续2个
            win_f = [win_number.f_1,win_number.f_2,win_number.f_3,win_number.f_4,
                     win_number.f_5]
            win_l = [win_number.l_1, win_number.l_2]
        else:
            win_f = [win_number.f_1, win_number.f_2, win_number.f_3, win_number.f_4,
                     win_number.f_5,win_number.f_6]
            win_l = [win_number.l_1]
        # 分区逐个检查
        f_cnt = 0  # 前区中间号码数
        l_cnt = 0  # 后区中间号码数
        for k in numbers["f"]:
            if k in win_f:
                f_cnt += 1
        for j in numbers["l"]:
            if j in win_l:
                l_cnt += 1
        print(win_f, win_l)
        print(f_cnt, l_cnt)
        if numbers["type"] == 1:
            return lottery_winning_rule(f_cnt, l_cnt)
        else:
            return double_chromosphere_winning_rule(f_cnt, l_cnt)


def updata_db_insert_new_record(data=None):
    # 把中奖信息记录到数据库
    if not data:
        return None
    # 字段对应：
    """
    f_num = {"01": "f_01", "02":"f_02", "03":"f_03","04":"f_04","05":"f_05","06":"f_06","07":"f_07","08":"f_08","09":"f_09", \
             "10":"f_10","11":"f_11","12":"f_12","13":"f_13","14":"f_14","15":"f_15","16":"f_16","17":"f_17","18":"f_18","19":"f_19", \
             "20":"f_20","21":"f_21","22":"f_22","23":"f_23","24":"f_24","25":"f_25","26":"f_26","27":"f_27","28":"f_28","29":"f_29", \
             "30":"f_30","31":"f_31","32":"f_32","33":"f_33","34":"f_34","35":"f_35"}
    b_num = {"01": "b_01", "02":"b_02", "03":"b_03","04":"b_04","05":"b_05","06":"b_06","07":"b_07","08":"b_08","09":"b_09", \
             "10":"b_10","11":"b_11","12":"b_12","13":"b_13","14":"b_14","15":"b_15","16":"b_16"}
    """
    with app.app_context():
        for lot_num in data["value"]:
            # 前区
            tmp_f = []
            tmp_f = data["value"][lot_num]["first"]
            tmp_l = []
            tmp_l = data["value"][lot_num]["last"]
            type = data['type']
            datestr = data["value"][lot_num]["draw_date"]
            # 1 大乐透； 2 双色球
            if type == 2:
                new_record = Numbers(lotteryDrawNum = str(lot_num), draw_date = datestr, type = data['type'], f_1 = tmp_f[0], f_2 = tmp_f[1], \
                                     f_3 = tmp_f[2], f_4 = tmp_f[3], f_5 = tmp_f[4], f_6 = tmp_f[5], l_1 = tmp_l[0])
            else:
                new_record = Numbers(lotteryDrawNum=str(lot_num), draw_date = datestr, type=data['type'], f_1=tmp_f[0], f_2=tmp_f[1], \
                                     f_3=tmp_f[2], f_4=tmp_f[3], f_5=tmp_f[4], l_1=tmp_l[0], l_2=tmp_l[1])
            db.session.merge(new_record)
        # 提交更改到数据库
        db.session.commit()

def analysis_sum(row=0, data=None):
    # 求和分析
    if not row:
        row = len(data)
    cnt = 0
    result = {}
    first_sum = 0
    last_sum = 0
    total_sum = 0
    for key in data:
        # 前区求和
        for j in data[key]["first"]:
            first_sum += int(j)
        # 后区求和
        for k in data[key]["last"]:
            last_sum += int(k)

        result[key] = {"first_sum": first_sum, "last_sum":last_sum}
        cnt += 1
        if cnt >row:
            break
    return result

def analysis_frequency(row=0, datas=None):
    # 数字出现的频率分析，在指定期数内，数字出现的次数统计，看看哪些是高频数字
    if not row:
        row = len(datas)
    cnt = 0
    first_freq = {}
    last_freq = {}
    for i in ("01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21", \
              "22","23","24","25","26","27","28","29","30","31","32","33","34","35"):
        first_freq[i] = 0

    # print("fuck")
    # print(datas)
    data = datas['value']
    # 如果是双色球，后区有16个取值范围；大乐透只有12个
    for k in data.keys():
        if len(data[k]["last"]) > 1:
            for j in ("01","02","03","04","05","06","07","08","09","10","11","12"):
                last_freq[j] = 0
        else:
            for j in ("01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16"):
                last_freq[j] = 0
        break

    for key in data:
        # 前区计算频率
        for j in data[key]["first"]:
            first_freq[j] = int(first_freq[j]) + 1
        # 后区计算频率
        for k in data[key]["last"]:
            last_freq[k] = int(last_freq[k]) + 1

        cnt += 1
        if cnt > row:
            break
    return {"first_freq":first_freq, "last_freq":last_freq}

def analysis_interval(row=0, data=None):
    # 数字出现的区间分析（把0~36分成N个区间，分析出现在各个区间的频率）
    pass


def HeatMap_analysis(freq_data=None):
    # 自定义 x 轴和 y 轴的数据
    x_data = ['A', 'B', 'C', 'D', 'E']
    y_data = ['1', '2', '3', '4', '5']

    # 创建数据
    data = []
    for x in x_data:
        for y in y_data:
            value = 0  # 这里替换为相应的值
            data.append([x, y, value])

    heatmap = (
        HeatMap()
        .add_xaxis(xaxis_data=x_data)
        .add_yaxis(
            "热力图",
            yaxis_data=y_data,
            value=data,
            label_opts=opts.LabelOpts(is_show=True, position="inside"),
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="自定义 X 轴和 Y 轴的热力图"),
            visualmap_opts=opts.VisualMapOpts(
                min_=0, max_=100, orient="horizontal", is_piecewise=True
            ),
        )
    )

    heatmap.render("custom_heatmap.html")


def line_analysis(freq_data=None):
    # 示例数据
    x_data = [1, 2, 3, 4, 5]
    y1_data = [1, 0, 1, 1, 0]
    y2_data = [28, 28, 0, 0, 0]
    y3_data = [35,0,35,35,0]

    # 创建折线图1
    line1 = (
        Line()
        .add_xaxis(x_data)
        .add_yaxis("Y1", y1_data, yaxis_index=0)
        .add_yaxis("Y2", y2_data, yaxis_index=0)
        .set_global_opts(
            title_opts=opts.TitleOpts(title="折线图1"),
        )
        .set_series_opts(
            label_opts=opts.LabelOpts(
                position="left"  # 自定义标签位置，可以选择 "top", "bottom", "left", "right", "inside", "insideTop", 等
            )
        )
    )

    # 创建折线图2
    line2 = (
        Line()
        .add_xaxis(x_data)
        .add_yaxis("Y3", y3_data, yaxis_index=1)
        .set_global_opts(
            title_opts=opts.TitleOpts(title="折线图2"),
        )
        .set_series_opts(
            label_opts=opts.LabelOpts(
                position="right"  # 自定义标签位置
            )
        )
    )

    # 使用 Grid 组件将两个折线图并列显示
    grid = (
        Grid()
        .add(line1, grid_opts=opts.GridOpts(pos_left="5%", pos_right="55%"))
        .add(line2, grid_opts=opts.GridOpts(pos_left="55%", pos_right="5%"))
    )

    # 渲染多个折线图到一个 HTML 文件
    grid.render("multiple_line_charts.html")


def radar(freq_data=None):
    # 雷达图
    v1_tmp = []
    v2_tmp = []
    max_num = 0
    schema_list = []
    for k in freq_data["first_freq"]:
        if freq_data["first_freq"][k] > max_num:
            max_num = freq_data["first_freq"][k]
    for k in freq_data["first_freq"]:
        schema_list.append(opts.RadarIndicatorItem(name=k, max_=max_num))
        v1_tmp.append(freq_data["first_freq"][k])
    for j in freq_data["last_freq"]:
        # schema.append(opts.RadarIndicatorItem(name=j, max_=max_num),)
        v2_tmp.append(freq_data["last_freq"][j])
    value1 = [v1_tmp]
    value2 = [v2_tmp]
    c = (
        Radar()
        .add_schema(
            schema=schema_list
        )
        .add("前区", value1)
        .add("后区", value2)
        .set_series_opts(label_opts=opts.LabelOpts(is_show=True))
        .set_global_opts(
            legend_opts=opts.LegendOpts(selected_mode="single"),
            title_opts=opts.TitleOpts(title="Radar-单例模式"),
        )
        .render("radar_selected_mode.html")
    )


if __name__ == '__main__':
    data = {}
    with app.app_context():
        db.create_all()
    # data = get_result_data(1, '大乐透中奖号码.txt')
    # data = get_result_data('双色球中奖号码.txt')
    # print(data['value'].keys())
    # 更新记录到数据库
    url_data = get_winner_data(2, 20)
    data = get_result_data(2, url_data)
    # updata_db_insert_new_record(data)
    # check_number = {"lotteryDrawNum":"2023041", "type":2, "f":[1, 6, 12, 13 ,24 ,15 ], "l":[9]}

    # choose_number = []
    # draw_qh = 2023131
    # check_number = {"lotteryDrawNum":str(draw_qh), "type":2, "f":[10,14,19,20,31,32], "l":[2]}
    # print(winning_check(check_number))
    freq_data = analysis_frequency(20, data)
    # 按出现频率从高到低排列
    freq_first_order = sorted(freq_data["first_freq"].items(), key=lambda x: x[1], reverse=True)
    freq_last_order = sorted(freq_data["last_freq"].items(), key=lambda x: x[1], reverse=True)
    # radar(freq_data)
    # HeatMap_analysis(freq_data)
    # print(freq_data)
    print(freq_first_order)
    print(freq_last_order)
